Reactive forms提供了一種model-driven
的方法來處理表單中會隨時間變化的輸入(數據),本篇中介紹著如何控制與更新一個表單、使用多個form-group、表單驗證等等的基本表單操作。
Reactive forms使用了顯式且不變
的方法管理表單狀況,對於表單的狀態的更改每次都會回傳一個新的表單狀態
,由於每次都會回傳一個新的狀態,所以他可以保持每次該改之間的model完整性,Reactive forms是圍繞著observable
所建立的,表單中的輸入(數值)會提供給input flow作為他的值並可以以同步的方式訪問。
Reactive forms與template forms不同,Reactive forms通過對數據模型的同步訪問提供了較高的可預測性
、observable operators的不變性
和可以透過observable streams觀察數據的變更
。
建立Reactive forms的三個步驟:
reactive foms module
,這個module中含有使用Reactive forms的指令。FormControl instance
。使用import關鍵字將ReactiveFormsModule
添加到app.module中的import陣列中。
// src/app/app.module.ts
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
// other imports ...
ReactiveFormsModule
],
})
export class AppModule { }
接下來透過cli建立一個 name-editor
Component,並將FormControl
class加入到Component中並將他實體化。
// src/app/name-editor/name-editor.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms'; // import FormControl Class
@Component({
selector: 'app-name-editor',
templateUrl: './name-editor.component.html',
styleUrls: ['./name-editor.component.css']
})
export class NameEditorComponent {
name = new FormControl(''); // instance FormControl class
}
在name-editor
的templete中建立可以與表單控制元件連動的網頁元件,使用formControl
綁定templete中的元件。
<!-- src/app/name-editor/name-editor.component.html -->
<label>
Name:
<input type="text" [formControl]="name">
</label>
在畫面中顯示出來
<!-- src/app/app.component.html -->
<app-name-editor></app-name-editor>
可以透過兩種方法顯示forms中的數值:
valueChange()
中的subscription()
方法監聽表單中的值。value
屬性。name = new FormControl('');
this.name.valueChanges.subscribe(val => console.log(val));
Value : {{name.value}}
一個表單中會包含幾個相關的控制元件,Reactive forms提供兩種方法可以將多個相關的控制組件分組為單個輸入形式
。
透過cli建立一個新組建,使用import將FormGroup
與FormControl
加入Component中並將他們實例化。
// src/app/profile-editor/profile-editor.component.ts (form group)
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms'; //import two class
@Component({
selector: 'app-profile-editor',
templateUrl: './profile-editor.component.html',
styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
// instance FormGroup & FormControl
profileForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl(''),
});
}
將firstName
與lastName
這兩個相關的控制組件透過FormGroup
分組為一個群組。
可以透過嵌套FromGroups來達到將較為複雜的表單內容分割為更小的Group,以方便管理以維護。
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-profile-editor',
templateUrl: './profile-editor.component.html',
styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
profileForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl(''),
address: new FormGroup({
street: new FormControl(''),
city: new FormControl(''),
state: new FormControl(''),
zip: new FormControl('')
})
});
}
將原本的profileForm Group中新增了第二層的嵌套Group,將street、city、state、zip綁定為一個FormGroup,透過這樣的綁定可以將原本的profileForm保持完整的前提下再新增出一個Group。
<form [formGroup]="profileForm">
<label>
First Name:
<input type="text" formControlName="firstName">
</label>
<label>
Last Name:
<input type="text" formControlName="lastName">
</label>
<div formGroupName="address">
<h3>Address</h3>
<label>
Street:
<input type="text" formControlName="street">
</label>
<label>
City:
<input type="text" formControlName="city">
</label>
<label>
State:
<input type="text" formControlName="state">
</label>
<label>
Zip Code:
<input type="text" formControlName="zip">
</label>
</div>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
當需要更新多個控制組件的值時,Angular提供兩種方法可以改變值,一種是可以更改整組表單的方法,另一種是更改表單部分內容的方法。
setValue()
可以更新整個表單內容,他必須要遵守整個表單的結構
。patchValue()
可以替換表單內的部分內容。使用setValue()會嚴格檢查整個表單的結構,若結構錯誤則會發生嵌套錯誤,若是使用patchValue()在這個錯誤上會默默地失敗。
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: {
street: '123 Drew Street'
}
});
}
對於在處理多個表單的時候還是使用手動的方式創建表單控制組件實例,這樣的話可能會遇到重複宣告的問題,所以可以使用FormBuilder
,它提供了用於生成控制組件的便利方法。
import { FormBuilder } from '@angular/forms';
在Component的comstructor中Inject FormBuilder service
。
constructor(private fb: FormBuilder) { }
FormBuilder提供control()
、group()
、array()
這三個方法,他們用於生成實例包括FormControl、FormGroup、FromArray。
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';
@Component({
selector: 'app-profile-editor',
templateUrl: './profile-editor.component.html',
styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
profileForm = this.fb.group({
firstName: [''],
lastName: [''],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
});
constructor(private fb: FormBuilder) { }
}
FormArray是用於管理任何數量的未命名控件的FormGroup的替代方法,可以動態的插入或刪除,所以不知道子值的數量時可以使用FormArray來代替FormGroup。
import { FormArray } from '@angular/forms';
通過在數組中定義控件,可以使用從零到許多的任意數量的控件來初始化表單數組,使用FormBuilder.array()
定義陣列並使用FormBuilder.control()
初始化控件填充數組。
profileForm = this.fb.group({
firstName: ['', Validators.required],
lastName: [''],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
aliases: this.fb.array([
this.fb.control('')
])
});
可以使用getter
可以輕鬆訪問表單陣列的實例的別名。
get aliases() {
return this.profileForm.get('aliases') as FormArray;
}
還可以使用FormArray.push()
將表單數據動態的插入到現有表單中。
addAlias() {
this.aliases.push(this.fb.control(''));
}
<div formArrayName="aliases">
<h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button>
<div *ngFor="let alias of aliases.controls; let i=index">
<!-- The repeated alias template -->
<label>
Alias:
<input type="text" [formControlName]="i">
</label>
</div>
</div>
使用*ngFor
遍歷aliases中的所有表單實例,因為表單數組元素未命名,所以您將索引分配給i變量,並將其傳遞給每個控件以將其綁定到formControlName輸入。
Class | Description |
---|---|
AbstractControl | FormControl,FormGroup和FormArray的抽象基類,它提供了常見的行為和屬性。 |
FormControl | 管理單個表單空間的值和有效性的狀態,他對應到HTML的表單控件<input> 或<select> |
FormGroup | 管理一組AbstractControl實例的值和有效狀態,它包含他這個FormGroup的子FormControl。 |
FormArray | 管理AbstractControl實例的陣列的值有效性。 |
FormBuilder | 用於創建FormControl的方法。 |
Directive | Description |
---|---|
FormControlDirective | 將獨立的FormControl實例同步到表單控件元素。 |
FormControlName | 通過名稱將現有FormGroup實例中的FormControl同步到表單控件元素。 |
FormGroupDirective | 將現有的FormGroup實例同步到DOM元素。 |
FormGroupName | 將嵌套的FormGroup實例同步到DOM元素。 |
FormArrayName | 將嵌套的FormArray實例同步到DOM元素。 |